﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Services;
using System.IO;
using System.Configuration;

using JigsawService.Parsers;
using JigsawService.Utility;
using JigsawService.DataAccess.Usage;

namespace JigsawService
{
    /// <summary>
    /// Uses PGP style encryption built into the application to secure the request message during transport
    /// </summary>
    [WebService(Namespace = "http://datametrix.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]

    public class EncSoapXml : System.Web.Services.WebService
    {
        JigsawBase jbase = new JigsawBase();
        APIMessage apimessage = new APIMessage();

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [WebMethod(Description = "Returns the current RSA public key as a string")]
        public string GetPublicKey()
        {
            string pubkey = "";
            apimessage.StateMessage = "EncSoapXml GetPublicKey: ";
            try
            {
                // get the path to the rsa key XML files
                string publickeypath = Server.MapPath(@"App_Data\rsapubkey.xml");

                if (File.Exists(publickeypath))
                {
                    using (TextReader tr = new StreamReader(publickeypath))
                    {
                        pubkey = tr.ReadToEnd();
                    }
                }
                else
                {
                    throw new Exception("Unable to access the public key file.");
                }
            }
            catch (Exception ex)
            {
                ErrorUtility errutil = new ErrorUtility();
                errutil.LogServerException(ex, apimessage);
            }

            return pubkey;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [WebMethod(Description = "Returns the current server time as milliseconds since Jan 1 1970")]
        public string GetServerTime()
        {
            string svrmillisec = "0";
            apimessage.StateMessage = "EncSoapXml GetServerTime: ";

            try
            {
                JigsawBase jbase = new JigsawBase();
                svrmillisec = jbase.GetTimeInMilliseconds();
            }
            catch (Exception ex)
            {
                ErrorUtility errutil = new ErrorUtility();
                errutil.LogServerException(ex, apimessage);
            }

            return svrmillisec;
        }

        /// <summary>
        /// Service controller using a combination of RSA asymmetric encryption and AES symmetric encryption
        /// </summary>
        /// <param name="symmKey">RSA encrypted symmetric key as base64 string</param>
        /// <param name="requestMessage">AES encrypted request message as base64 string</param>
        /// <returns></returns>
        [WebMethod(Description = "Accepts encrypted XML request messages, and returns encrpted XML response messages")]
        public string Controller(string symmKey, string requestMessage)
        {
            apimessage.StateMessage = "EncXmlSoap Controller: ";

            string clearSymmKey = "";
            string clearRequest = "";
            string responseMessage = "";
            string encryptedResponse = "";

            XMLParser xmlparser = new XMLParser(apimessage);
            apimessage.StartTime = jbase.GetTimeInMilliseconds();

            try
            {
                apimessage.Location = ConfigurationManager.AppSettings["Location"].ToString();
                
                // decrypt symKey with RSA private key
                clearSymmKey = DecryptSymKey(symmKey);

                // decrypt paramStr with symKey
                clearRequest = DecryptParameters(clearSymmKey, requestMessage);

                // process request and receive response message as return
                xmlparser.ProcessAPIRequest(clearRequest);
            }
            catch (Exception ex)
            {
                ErrorUtility errutil = new ErrorUtility();
                errutil.LogServerException(ex, apimessage);
                jbase.SetExceptionResult(ex.Message, apimessage);
            }

            try
            {
                apimessage.EndTime = jbase.GetTimeInMilliseconds();
                responseMessage = xmlparser.SerializeXMLResponse();

                // API usage data logging
                if (ConfigurationManager.AppSettings["UsageLogging"].ToString().ToUpper() == "ON")
                {
                    LogUsageData();
                }
            }
            catch (Exception ex)
            {
                // do not allow logging errors to interfere with results of API method call
                LogUtility lutil = new LogUtility();
                lutil.WriteToEventLog("EncSoapxml LogUsage: " + ex.Message);

                ErrorUtility eutil = new ErrorUtility();
                eutil.WriteToDatabase("", ex.Message, apimessage);
            }

            // encrypt the response message
            encryptedResponse = EncryptResults(clearSymmKey, responseMessage);

            // return encrypted response to the client application
            return encryptedResponse;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="cipherText"></param>
        /// <returns></returns>
        protected string DecryptSymKey(string cipherText)
        {
            apimessage.StateMessage = "EncXmlSoap DecryptSymKey: ";
            string symKey = "";
            string rsaKeyPair = "";

            // get the path to the rsa key XML files
            string privatekeypath = Server.MapPath(@"App_Data\rsakeypair.xml");

            // read the RSA xml key files
            if (File.Exists(privatekeypath))
            {
                using (TextReader tr = new StreamReader(privatekeypath))
                {
                    rsaKeyPair = tr.ReadToEnd();
                }
            }
            else
            {
                throw new Exception("Unable to access key pair.");
            }

            EncryptionUtility encutil = new EncryptionUtility();
            if (rsaKeyPair.Length > 0)
            {
                symKey = encutil.RSADecryptKeyString(rsaKeyPair, cipherText);
            }
            else
            {
                throw new Exception("Unable to read key pair.");
            }
            return symKey;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="symKey"></param>
        /// <param name="cipherText"></param>
        /// <returns></returns>
        protected string DecryptParameters(string symKey, string cipherText)
        {
            apimessage.StateMessage = "EncXmlSoap DecryptParameters: ";
            string paramStr = "";
            EncryptionUtility encutil = new EncryptionUtility();
            paramStr = encutil.DecryptString2(cipherText, symKey);
            return paramStr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="symKey"></param>
        /// <param name="clearText"></param>
        /// <returns></returns>
        protected string EncryptResults(string symKey, string clearText)
        {
            apimessage.StateMessage = "EncXmlSoap EncryptResults: ";
            string cipherText = "";
            EncryptionUtility encutil = new EncryptionUtility();
            cipherText = encutil.EncryptString2(clearText, symKey);
            return cipherText;
        }

        /// <summary>
        /// Test for user and method filter values
        /// </summary>
        private void LogUsageData()
        {
            apimessage.StateMessage = "EncXmlSoap LogUsageData: ";
            string users = ConfigurationManager.AppSettings["UserList"].ToString();
            string methods = ConfigurationManager.AppSettings["MethodList"].ToString();

            bool userOK = false;
            bool methOK = false;

            // check account filter value
            if (users == "*")
            {
                userOK = true;
            }
            else
            {
                List<string> userList = new List<string>(users.Split(new char[] { ',' }));
                if (userList.Contains(apimessage.UserName)) userOK = true;
            }

            // check method filter value
            if (methods == "*")
            {
                methOK = true;
            }
            else
            {
                List<string> methList = new List<string>(methods.Split(new char[] { ',' }));
                if (methList.Contains(apimessage.APIString)) methOK = true;
            }

            // if account filter and method filter both passed, log duation metric
            if (userOK && methOK)
            {
                SaveUsage();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected void SaveUsage()
        {
            apimessage.StateMessage = "EncXmlSoap SaveUsage: ";

            APIUsageDA apiuda = new APIUsageDA(apimessage);
            apiuda.APIUsage();
        }

    }
}
